package parser;

import base.*;
import expressions.*;
import instructions.*;
import slip.trees.TreeNode;
import values.*;
import values.Integer;
import values.Boolean;

public class ST2AST {
	private static Instruction trad27(TreeNode tree) {
		// tree symbol is <statementblock>

		int r = tree.getRule();
		switch (r) {
		case 0: // <statementblock> --> <statement> <statementblockend>
		{
			Instruction x0 = trad29(tree.getChild(0));
			Instruction x1 = trad28(tree.getChild(1));
			if (x1 == null)
				return x0;
			else
				return new Composition(x0, x1);
		}
		default:
			return null;
		}
	}

	private static Instruction trad28(TreeNode tree) {
		// tree symbol is <statementblockend>

		int r = tree.getRule();
		switch (r) {
		case 0: // <statementblockend> --> <lambda>
		{
			return null; // a modifier
		}
		case 1: // <statementblockend> --> <statementblock>
		{
			return trad27(tree.getChild(0));
		}
		default:
			return null;
		}
	}

	private static Instruction trad29(TreeNode tree) {
		// tree symbol is <statement>

		int r = tree.getRule();
		switch (r) {
		case 0: // <statement> --> { <statementblock> }
		{
			return trad27(tree.getChild(1));
		}
		case 1: // <statement> --> <instr> ;
		{
			return trad45(tree.getChild(0));
		}
		case 2: // <statement> --> LOCAL ( <identifier> ) { <statementblock> }
		{
			Identifier x2 = trad40(tree.getChild(2));
			Instruction x5 = trad27(tree.getChild(5));
			return new Local(x2, x5); // a modifier
		}
		case 3: // <statement> --> IF ( <expression> ) { <statementblock> } ELSE
				// { <statementblock> }
		{
			Expression x2 = trad41(tree.getChild(2));
			Instruction x5 = trad27(tree.getChild(5));
			Instruction x9 = trad27(tree.getChild(9));
			return new If(x2, x5, x9); // a modifier
		}
		case 4: // <statement> --> DEF <identifier> ( <parameterList> ) {
				// <statementblock> }
		{
			Identifier x1 = trad40(tree.getChild(1));
			Identifier[] x3 = trad31(tree.getChild(3));
			Instruction x6 = trad27(tree.getChild(6));
			return new FunctionDef(x1, x6, x3);
		}
		case 5: // <statement> --> WHILE ( <expression> ) { <statementblock> }
		{
			Expression x2 = trad41(tree.getChild(2));
			Instruction x5 = trad27(tree.getChild(5));
			return new While(x2, x5);
		}
		case 6: // <statement> --> CASE <identifier> OF <pattern> {
				// <statementblock> } ELSE { <statementblock> }
		{
			AST x1 = trad40(tree.getChild(1));
			AST x3 = trad35(tree.getChild(3));
			Instruction x5 = trad27(tree.getChild(5));
			Instruction x9 = trad27(tree.getChild(9));
			return new Case(x1, x3, x5, x9); // a modifier
		}
		default:
			return null;
		}
	}

	private static Identifier[] trad30(TreeNode tree) {
		// tree symbol is <parameterListend>

		int r = tree.getRule();
		switch (r) {
		case 0: // <parameterListend> --> <identifier> <parameterList>
		{
			Identifier x0 = trad40(tree.getChild(0));
			Identifier[] x1 = trad31(tree.getChild(1));
			if (x1 == null) {
				Identifier temp[] = { x0 };
				return temp;
			} else {
				Identifier temp[] = new Identifier[x1.length + 1];
				temp[0] = x0;
				for (int i = 0; i < x1.length; i++)
					temp[i + 1] = x1[i];
				return temp;
			}

		}
		default:
			return null;
		}
	}

	private static Identifier[] trad31(TreeNode tree) {
		// tree symbol is <parameterList>

		int r = tree.getRule();
		switch (r) {
		case 0: // <parameterList> --> <lambda>
		{
			return null;
		}
		case 1: // <parameterList> --> <parameterListend>
		{
			return trad30(tree.getChild(0));
		}
		default:
			return null;
		}
	}

	private static AST trad32(TreeNode tree) {
		// tree symbol is <val>

		int r = tree.getRule();
		switch (r) {
		case 0: // <val> --> <number>
		{
			Integer x0 = trad33(tree.getChild(0));
			return x0;
		}
		case 1: // <val> --> <record>
		{
			AST[] x0 = trad34(tree.getChild(0));
			return new ArrayExpr(x0);
		}
		case 2: // <val> --> <boolean>
		{
			Boolean x0 = trad36(tree.getChild(0));
			return x0;
		}
		case 3: // <val> --> <expression>
		{
			Expression x0 = trad41(tree.getChild(0));
			return x0;
		}
		case 4: // <val> --> <identifier>
		{
			Identifier x0 = trad40(tree.getChild(0));
			return new VariableAccess(x0);
		}
		case 5: // <val> --> <string>
		{
			String x0 = trad37(tree.getChild(0));
			return new FantaString(x0);
		}
		case 6: // <val> --> SCAN
		{
			return new Input();
		}
		default:
			return null;
		}
	}

	private static Integer trad33(TreeNode tree) {
		// tree symbol is <number>

		int r = tree.getRule();
		switch (r) {
		case 0: // <number> --> NUMBER
		{
			return new Integer(tree.getChild(0).getIntValue());
		}
		default:
			return null;
		}
	}

	private static AST[] trad34(TreeNode tree) {
		// tree symbol is <record>

		int r = tree.getRule();
		switch (r) {
		case 0: // <record> --> [ <valList> ]
		{
			AST[] x1 = trad39(tree.getChild(1));
			return x1;
		}
		default:
			return null;
		}
	}

	private static AST trad35(TreeNode tree) {
		// tree symbol is <pattern>

		int r = tree.getRule();
		switch (r) {
		case 0: // <pattern> --> <identifier>
		{
			Identifier x0 = trad40(tree.getChild(0));
			return x0; // a modifier
		}
		case 1: // <pattern> --> <record>
		{
			AST[] x0 = trad34(tree.getChild(0));
			for (int i = 0; i < x0.length; i++)
				if (x0[i] instanceof VariableAccess)
					x0[i] = ((VariableAccess) x0[i]).getIdentifier();
			return new PatternArray(x0);
		}
		default:
			return null;
		}
	}

	private static Boolean trad36(TreeNode tree) {
		// tree symbol is <boolean>

		int r = tree.getRule();
		switch (r) {
		case 0: // <boolean> --> TRUE
		{
			return new Boolean(true);
		}
		case 1: // <boolean> --> FALSE
		{
			return new Boolean(false);
		}
		default:
			return null;
		}
	}

	private static String trad37(TreeNode tree) {
		// tree symbol is <string>

		int r = tree.getRule();
		switch (r) {
		case 0: // <string> --> STRING
		{
			return tree.getChild(0).getStringValue();
		}
		default:
			return null;
		}
	}

	private static AST[] trad38(TreeNode tree) {
		// tree symbol is <valListend>

		int r = tree.getRule();
		switch (r) {
		case 0: // <valListend> --> <val> <valList>
		{
			AST x0 = trad32(tree.getChild(0));
			AST[] x1 = trad39(tree.getChild(1));
			if (x1 == null) {
				AST temp[] = { x0 };
				return temp;
			} else {
				AST temp[] = new AST[x1.length + 1];
				temp[0] = x0;
				for (int i = 0; i < x1.length; i++)
					temp[i + 1] = x1[i];
				return temp;
			}

		}
		default:
			return null;
		}
	}

	private static AST[] trad39(TreeNode tree) {
		// tree symbol is <valList>

		int r = tree.getRule();
		switch (r) {
		case 0: // <valList> --> <lambda>
		{
			return null;
		}
		case 1: // <valList> --> <valListend>
		{
			return trad38(tree.getChild(0));
		}
		default:
			return null;
		}
	}

	private static Identifier trad40(TreeNode tree) {
		// tree symbol is <identifier>

		int r = tree.getRule();
		switch (r) {
		case 0: // <identifier> --> IDENTIFIER
		{
			return new Identifier(tree.getChild(0).getStringValue()); // a
																		// modifier
		}
		default:
			return null;
		}
	}

	private static Expression trad41(TreeNode tree) {
		// tree symbol is <expression>

		int r = tree.getRule();
		switch (r) {
		case 0: // <expression> --> <unary operator> <val>
		{
			String x0 = trad42(tree.getChild(0));
			AST x1 = trad32(tree.getChild(1));

			if (x0.equals("~"))
				return new Substract(new Integer(0), x1);
			else if (x0.equals("!"))
				return new NotGate(x1);
			else
				return null;
		}
		case 1: // <expression> --> <binary operator> <val> <val>
		{
			String x0 = trad43(tree.getChild(0));
			AST x1 = trad32(tree.getChild(1));
			AST x2 = trad32(tree.getChild(2));

			if (x0.equals("-"))
				return new Substract(x1, x2);
			else if (x0.equals("+"))
				return new Add(x1, x2);
			else if (x0.equals("*"))
				return new Multiply(x1, x2);
			else if (x0.equals("/"))
				return new Divide(x1, x2);
			else if (x0.equals("%"))
				return new Modulo(x1, x2);
			else if (x0.equals("||"))
				return new OrGate(x1, x2);
			else if (x0.equals("&&"))
				return new AndGate(x1, x2);
			else if (x0.equals("<"))
				return new Less(x1, x2);
			else if (x0.equals("<="))
				return new LessEqual(x1, x2);
			else if (x0.equals(">"))
				return new Greater(x1, x2);
			else if (x0.equals(">="))
				return new GreaterEqual(x1, x2);
			else if (x0.equals("=="))
				return new Equal(x1, x2);
			else if (x0.equals("!="))
				return new Different(x1, x2);
			else if (x0.equals("[]?") && x1 instanceof VariableAccess)
				return new ArrayAccess(((VariableAccess) x1).getIdentifier(),
						x2);
			else
				return null;

		}
		case 2: // <expression> --> ( <expression> )
		{
			return trad41(tree.getChild(1));
		}
		default:
			return null;
		}
	}

	private static String trad42(TreeNode tree) {
		// tree symbol is <unary operator>

		int r = tree.getRule();
		switch (r) {
		case 0: // <unary operator> --> UNARYOP
		{
			return tree.getChild(0).getStringValue();
		}
		default:
			return null;
		}
	}

	private static String trad43(TreeNode tree) {
		// tree symbol is <binary operator>

		int r = tree.getRule();
		switch (r) {
		case 0: // <binary operator> --> BINARYOP
		{
			return tree.getChild(0).getStringValue();
		}
		default:
			return null;
		}
	}

	private static String trad44(TreeNode tree) {
		// tree symbol is <trinary operator>

		int r = tree.getRule();
		switch (r) {
		case 0: // <trinary operator> --> TRINARYOP
		{
			return tree.getChild(0).getStringValue();
		}
		default:
			return null;
		}
	}

	private static Instruction trad45(TreeNode tree) {
		// tree symbol is <instr>

		int r = tree.getRule();
		switch (r) {
		case 0: // <instr> --> <identifier> = <val>
		{
			Identifier x0 = trad40(tree.getChild(0));
			AST x2 = trad32(tree.getChild(2));
			return new VariableAssign(x0, x2);
		}
		case 1: // <instr> --> <functionCall>
		{
			FunctionCall x0 = trad46(tree.getChild(0));
			return x0;
		}
		case 2: // <instr> --> PRINT <val>
		{
			AST x1 = trad32(tree.getChild(1));
			return new Print(x1);
		}
		case 3: // <instr> --> <trinary operator> <identifier> <val> <val>
		{
			String x0 = trad44(tree.getChild(0));
			Identifier x1 = trad40(tree.getChild(1));
			AST x2 = trad32(tree.getChild(2));
			AST x3 = trad32(tree.getChild(3));
			if (x0.equals("[]!"))
				return new ArrayAssign(x1, x2, x3);
		}
		case 4: // <instr> --> SKIP
		{
			return new Skip(); // a modifier
		}
		default:
			return null;
		}
	}

	private static FunctionCall trad46(TreeNode tree) {
		// tree symbol is <functionCall>

		int r = tree.getRule();
		switch (r) {
		case 0: // <functionCall> --> CALL <identifier> ( <valList> )
		{
			Identifier x1 = trad40(tree.getChild(1));
			AST[] x3 = trad39(tree.getChild(3));
			if (x3 == null) {
				return new FunctionCall(x1, null, null);
			}
			Identifier[] identifiers = new Identifier[x3.length];
			AST[] expressions = new AST[x3.length];

			for (int i = 0; i < x3.length; i++) {
				if (x3[i] instanceof Identifier) {
					identifiers[i] = (Identifier) x3[i];
					expressions[i] = new VariableAccess((Identifier) x3[i]);
				} else {
					identifiers[i] = null;
					expressions[i] = x3[i];
				}
			}

			return new FunctionCall(x1, identifiers, expressions);
		}
		default:
			return null;
		}
	}

	public static Instruction tradProgram(TreeNode tree) throws Exception {
		return trad27(tree);
	}

}
